#include "gameroom.h"
#include"gameselect.h"
#include<QPainter>
#include<QPixmap>
#include<QIcon>
#include<QTimer>
#include<QPushButton>
#include<QMessageBox>
#include<QFile>
#include<QTextStream>
// 游戏房间
GameRoom::GameRoom(QWidget *parent) : QWidget(parent)
{
    // 3.1 设置窗口属性
    this->setFixedSize(1000,800);
    this->setWindowIcon(QIcon(":/res/ico.png"));
    this->setWindowTitle("游戏房间");


    // 3.2 初始化贪吃蛇
    // 初始化贪吃蛇链表，在窗口中间出现
    SnakeList.push_back(QRectF(this->width() * 0.5,this->height() * 0.5,KSnakeNodeWidth,KSnakeNodeHeight));
    moveUp();
    moveUp();


    // 3.3 创建食物
    createNewFood();

    // 3.4 定时器控制蛇的移动
    timer = new QTimer(this);
    // 用 connect
    connect(timer,&QTimer::timeout,[=](){
        // 3.4.1 处理食物和蛇头相交
        int cnt = 1;// 记录食物出现的次数

        // 判断蛇与食物是否相交
        if(SnakeList.front().intersects(food))
        {
            createNewFood();
            cnt++;

            // 添加吃食物的音效
            QSound::play(":/res/eatfood.wav");
        }

        // 3.4.2 处理蛇的吃完食物增加节点的方向
        while(cnt--)
        {
            switch(moveDirect)
            {
            case SnakeDirect::UP:
                moveUp();
                break;
            case SnakeDirect::DOWN:
                moveDown();
                break;
            case SnakeDirect::LEFT:
                moveLeft();
                break;
            case SnakeDirect::RIGHT:
                moveRight();
                break;
            }
        }


        // 蛇移动的原理：是蛇从蛇头开始，蛇头先移动，之后后面的每一个节点移动，而最后一个节点则不移动，
        // 在蛇的移动完的节点后面增加一个节点，最后删除原先的没有移动的最后一个节点
        SnakeList.pop_back();// 删除最后一个节点
        update();
    });


    // 3.5 设置 开始游戏 暂停游戏
    // 用按钮来控制
    QPushButton *startButton = new QPushButton(this);
    QPushButton *stopButton = new QPushButton(this);
    startButton->move(820,100);
    stopButton->move(820,170);
    startButton->setText("开始游戏");
    stopButton->setText("暂停游戏");

    // 设置字体
    QFont font("楷体",22);
    startButton->setFont(font);
    stopButton->setFont(font);

    // 链接 按钮和槽
    connect(startButton,&QPushButton::clicked,[=](){
        // 开始游戏
        isGameStart = true;
        timer->start(moveTimeOut);// 定时器开始

        sound = new QSound(":/res/Trepak.wav");
        sound->play();
        sound->setLoops(-1);// 持续播放

    });

    // 暂停游戏
    connect(stopButton,&QPushButton::clicked,[=](){
        isGameStart = false;
        timer->stop();
        sound->stop();
    });

    // 添加快捷键

    startButton->setShortcut(QKeySequence(Qt::Key_Enter));
    stopButton->setShortcut(QKeySequence(Qt::Key_Space));


    // 3.6 方向控制 上 下 左 右
    QPushButton *up = new QPushButton(this);
    QPushButton *down = new QPushButton(this);
    QPushButton *left = new QPushButton(this);
    QPushButton *right = new QPushButton(this);
    up->setText("👆");
    down->setText("👇");
    left->setText("👈");
    right->setText("👉");

    // 设置快捷键
    up->setShortcut(QKeySequence(Qt::Key_Up));
    down->setShortcut(QKeySequence(Qt::Key_Down));
    left->setShortcut(QKeySequence(Qt::Key_Left));
    right->setShortcut(QKeySequence(Qt::Key_Right));

    up->move(860,350);
    down->move(870,450);
    left->move(820,400);
    right->move(910,400);

    QFont font2("宋体",35);
    up->setFont(font2);
    down->setFont(font2);
    left->setFont(font2);
    right->setFont(font2);

    // 设置按钮样式
    up->setStyleSheet("QPushButton{ border : 0px;}");
    down->setStyleSheet("QPushButton{ border : 0px;}");
    left->setStyleSheet("QPushButton{ border : 0px;}");
    right->setStyleSheet("QPushButton{ border : 0px;}");

    // 设置 按钮与槽
    // 👆
    connect(up,&QPushButton::clicked,[=](){
        if(moveDirect != SnakeDirect::DOWN)
        {
            moveDirect = SnakeDirect::UP;
        }
    });

    // 👇
    connect(down,&QPushButton::clicked,[=](){
        if(moveDirect != SnakeDirect::UP)
        {
            moveDirect = SnakeDirect::DOWN;
        }
    });

    // 👈
    connect(left,&QPushButton::clicked,[=](){
        if(moveDirect != SnakeDirect::RIGHT)
        {
            moveDirect = SnakeDirect::LEFT;
        }
    });

    // 👉
    connect(right,&QPushButton::clicked,[=](){
        if(moveDirect != SnakeDirect::LEFT)
        {
            moveDirect = SnakeDirect::RIGHT;
        }
    });


    // 3.7 设置退出游戏按钮，给出弹窗
    QPushButton *exitButton = new QPushButton(this);
    exitButton->setText("退出游戏");
    exitButton->move(820,730);
    exitButton->setFont(QFont("楷体",22));

    // 设置一个弹窗
    QMessageBox *message = new QMessageBox();
    QPushButton *ok = new QPushButton("OK");
    QPushButton *canel = new QPushButton("Canel");

    message->setIcon(QMessageBox::Question);
    message->addButton(ok,QMessageBox::AcceptRole);
    message->addButton(canel,QMessageBox::RejectRole);
    message->setWindowTitle("退出游戏");
    message->setText("确认退出游戏");
    message->setWindowIcon(QIcon(":/res/ico.png"));

    connect(exitButton,&QPushButton::clicked,[=](){
        message->show();
        message->exec();// 事件轮询

        QSound::play(":/res/clicked.wav");

        // 判断鼠标点击的按钮
        if(message->clickedButton() == ok)
        {
            this->close();
        }
        else
        {
            message->close();
        }
    });


    // 3.8 添加一个按钮使之返回 游戏选择界面
    QPushButton *backButton = new QPushButton(this);
    backButton->setIcon(QIcon(":/res/back.png"));
    backButton->move(870,650);
    backButton->setIconSize(QSize(50,50));


    connect(backButton,&QPushButton::clicked,[=](){
        QSound::play(":/res/clicked.wav");
        // 关闭 游戏房间 界面
        this->close();

    GameSelect * gameselect = new GameSelect();
        // 打开 游戏选择 界面
        gameselect->show();

    });

}

// 绘制游戏房间背景
void GameRoom::paintEvent(QPaintEvent *event)
{
    (void) event;

    QPainter painter(this);
    QPixmap pix;
    // 1.添加游戏背景画面
    pix.load(":/res/game_room");
    painter.drawPixmap(0,0,800,800,pix);

    //2. 添加游戏操作界面
    pix.load(":/res/bg1.png");
    painter.drawPixmap(800,0,210,900,pix);



    // 3.绘制蛇：蛇头 + 蛇身体 + 蛇尾巴
    // 3.1 绘制蛇头：上 下 左 右 各不一样
    if(moveDirect == SnakeDirect::UP)
    {
        pix.load(":/res/up.png");// 加载图片
    }
    else if(moveDirect == SnakeDirect::DOWN)
    {
        pix.load(":/res/down.png");// 加载图片
    }
    else if(moveDirect == SnakeDirect::LEFT)
    {
        pix.load(":/res/left.png");// 加载图片
    }
    else if(moveDirect == SnakeDirect::RIGHT)
    {
        pix.load(":/res/right.png");// 加载图片
    }

    // 没有定义，随机位置
    auto snakeHead = SnakeList.front();
    painter.drawPixmap(snakeHead.x(),snakeHead.y(),snakeHead.width(),snakeHead.height(),pix);

    // 3.2 绘制蛇身
    pix.load(":/res/Bd.png");
    for (int i = 1;i < SnakeList.size() - 1; i++)
    {
        auto node = SnakeList.at(i);
        painter.drawPixmap(node.x(),node.y(),node.width(),node.height(),pix);
    }

    // 3.3 绘制蛇尾巴
    // 获得蛇链表的最后一个节点
    auto snakeLast = SnakeList.back();
    painter.drawPixmap(snakeLast.x(),snakeLast.y(),snakeLast.width(),snakeLast.height(),pix);


    // 4. 绘制食物
    pix.load(":/res/food.bmp");
    painter.drawPixmap(food.x(),food.y(),food.width(),food.height(),pix);

    // 5. 绘制分数
    pix.load(":/res/sorce_bg.png");
    painter.drawPixmap(this->width()*0.85,this->height()*0.05,120,50,pix);

    // 5.1 设置字体颜色
    QPen pen;
    pen.setColor(Qt::black);
    painter.setPen(pen);

    QFont font("方正舒体",22);
    painter.setFont(font);

    // 5.2 绘制分数文本
    painter.drawText(this->width()*0.92,this->height()*0.098,QString("%1").arg(SnakeList.size()));

    // 5.3 将分数文本写到这个文件中
    QFile file("C:\\Users\\lenovo\\Desktop\\code\\qt\\1.txt");
    if(file.open(QIODevice::WriteOnly | QIODevice::Text))// 写入
    {
        QTextStream out(&file);
        int num = SnakeList.size();
        out << num;// 写入文件
        file.close();
    }


    // 6. 绘制游戏失败效果
    if(checkFail())
    {
        QFont font("方正舒体",70);
        painter.setFont(font);

        pen.setColor(Qt::red);
        painter.setPen(pen);

        painter.drawText(this->width()*0.15,this->height()*0.5,QString("GAME OVER!"));
        QSound::play(":/res/gameover.wav");

        timer->stop();

        // 关闭游戏音效
        sound->stop();
    }
}

// 蛇的向上移动
void GameRoom::moveUp()
{
    // 1.定义两个浮点数类型的 Point 点
    // 一个点表示蛇单个节点的左上角节点的坐标
    QPointF leftTop;
    // 另一个点表示蛇单个节点的右下角节点的坐标
    QPointF rightBottom;

    // 2.找出蛇头
    auto snakeNode = SnakeList.front();

    // 定义左上角节点的 x，y
    int headX = snakeNode.x();
    int headY = snakeNode.y();
    // 两种情况：
    // 2.1 蛇头出窗口范围时
    // 则就直接从窗口的下面出现
    if(headY < 0)
    {
        leftTop = QPointF(headX,this->height() - KSnakeNodeHeight);// 窗口高度减去蛇节点的本身高度
    }else// 2.2 蛇头在窗口范围时
    {
        leftTop = QPointF(headX,headY - KSnakeNodeHeight);// 节点向上走一个节点高度
    }

    // 3.由 leftTop 推出 rightBottom
    rightBottom = QPointF(leftTop.x() + KSnakeNodeWidth,leftTop.y() + KSnakeNodeHeight);

    // 4.更新蛇链表
    SnakeList.push_front(QRectF(leftTop,rightBottom));



}

// 蛇向下移动
void GameRoom::moveDown()
{
    // 1.定义两个浮点数类型的 Point 点
    // 一个点表示蛇单个节点的左上角节点的坐标
    QPointF leftTop;
    // 另一个点表示蛇单个节点的右下角节点的坐标
    QPointF rightBottom;

    // 2.找到蛇头
    auto snakeNode = SnakeList.front();
    int headX = snakeNode.x();
    int headY = snakeNode.y();

    // 3. 两种情况：
    // 2.1 蛇头出窗口范围时
    // 则就直接从窗口的上面出现
    // 当向下移动时，leftTop 即表示矩形的左上角，rightBottom 即表示矩形的右下角
    if(headY > this->height())
    {
        leftTop = QPointF(headX,0);// 从窗口上面出现
    }
    else// 2.2 蛇头在窗口范围时
    {
        leftTop = snakeNode.bottomLeft();// 返回矩形左下角坐标，即向下移动HeadY位置
    }

    // 4.更新 rightBottom
    rightBottom = leftTop + QPointF(KSnakeNodeWidth,KSnakeNodeHeight);

    // 5.更新链表
    SnakeList.push_front(QRectF(leftTop,rightBottom));
}

// 向左移动
void GameRoom::moveLeft()
{
    // 1.定义两个浮点数类型的 Point 点
    // 一个点表示蛇单个节点的左上角节点的坐标
    QPointF leftTop;
    // 另一个点表示蛇单个节点的右下角节点的坐标
    QPointF rightBottom;

    // 2.找到蛇头
    auto snakeNode = SnakeList.front();
    int headX = snakeNode.x();
    int headY = snakeNode.y();

    // 3. 两种情况：
    // 2.1 蛇头从左边出窗口范围时
    // 则就直接从窗口的最右边出现
    if(headX < 0)
    {
        // 蛇可以移动的窗口大小是 800*800
        leftTop = QPointF(800 - KSnakeNodeWidth,headY);
    }
    else// 2.2 蛇头没有出窗口范围时
    {
        leftTop = QPointF(headX - KSnakeNodeWidth, headY);
    }

    // 4.更新 rightBottom
    rightBottom = leftTop + QPointF(KSnakeNodeWidth,KSnakeNodeHeight);

    // 5.更新链表
    SnakeList.push_front(QRectF(leftTop,rightBottom));

}

// 向右移动
void GameRoom::moveRight()
{
    // 1.定义两个浮点数类型的 Point 点
    // 一个点表示蛇单个节点的左上角节点的坐标
    QPointF leftTop;
    // 另一个点表示蛇单个节点的右下角节点的坐标
    QPointF rightBottom;

    // 2.找到蛇头
    auto snakeNode = SnakeList.front();
    int headX = snakeNode.x();
    int headY = snakeNode.y();

    // 3. 两种情况：
    // 2.1 蛇头从最右边出窗口范围时
    // 则就直接从窗口的最左边出现
    if(headX > 762)
    {
        leftTop = QPointF(0,headY);
    }
    else// 2.2 蛇头没有出窗口范围
    {
        //leftTop = QPointF(headX + KSnakeNodeWidth, headY);
        leftTop = snakeNode.topRight();// 变成右上角的坐标
    }

    // 4.更新 rightBottom
    rightBottom = leftTop + QPointF(KSnakeNodeWidth,KSnakeNodeHeight);

    // 5.更新链表
    SnakeList.push_front(QRectF(leftTop,rightBottom));
}

// 判断游戏是否结束
// 蛇自己是否撞到自己
bool GameRoom::checkFail()
{
    // 遍历整个链表，看节点是否一致，若一致则就撞到，游戏结束
    for (int i = 0;i < SnakeList.size();i++)
    {
        for (int j = i + 1;j < SnakeList.size();j++)
        {
            // 游戏失败，返回 true
            if(SnakeList.at(i) == SnakeList.at(j))
            {
                return true;
            }
        }
    }

    return false;
}

// 创建食物，随机产生
void GameRoom::createNewFood()
{
    food = QRectF(qrand() % (800 / KSnakeNodeWidth) * KSnakeNodeHeight,
                  qrand() % (800 / KSnakeNodeHeight) * KSnakeNodeWidth,
                  KSnakeNodeWidth,KSnakeNodeHeight);
}

